package com.detroitchow2;

/** 
 * StoreService
 * HIgh level sinegleton that provides data to Activties, responsible for retrieving data from detroitchow.com
 * storing data to database, fullfilling getData requests from Activities
 * 
 * Note: Activites reigster and unregister themselves with StoreService
 * 
 * Note:
 * Used to impplement a Sqlite database but populating DB performance sucked..
 * "Database" is now a large Array of TagCollections that is serialized to the location of STORAGE_FILE and persists in memory when the app is alive
 * 
 */

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;


import com.detroitchow2.api.IArticleUpdateObserver;
import com.detroitchow2.api.IFetchArticlesObserver;
import com.detroitchow2.api.IFetchStoresObserver;
import com.detroitchow2.api.IStoreServiceObserver;
import com.detroitchow2.tasks.FetchAllStoresTask;
import com.detroitchow2.tasks.FetchArticlesTask;
import com.detroitchow2.util.AppUtil;
import com.detroitchow2.util.LogUtil;
import com.detroitchow2.vo.Article;
import com.detroitchow2.vo.StoreVO;
import com.detroitchow2.vo.TagCollection;

import android.app.Application;
import android.content.Context;

public class StoreService extends Application 
  implements IFetchStoresObserver, IFetchArticlesObserver
  {
  
    //DatabaseAdapter dbAdapter;
    //private int storesInDB=0;
    private ArrayList<IStoreServiceObserver> storesObservers;
    private ArrayList<IArticleUpdateObserver> articleObservers;
    private ArrayList<String> tags = new ArrayList<String>();
    public static final String ALL_TAG="ALL";
    
    private ArrayList<TagCollection> masterList=null;
    private ArrayList<StoreVO> allList=null;
    private ArrayList<StoreVO> mapList=null;
    private ArrayList<StoreVO> tagList=null;
    
    public static final String SELECTED_STORE_KEY="DetroitChowSetSelectedStore";
    public static final String SELECTED_STORE_NAME="DetroitChowSetSelectedStoreName";
    public static final String SELECTED_STORE_OVERRIDE="DetroitChowSetSelectedOverride";
    public static final String ACTIVITY_KEY="DetroitChowActivityKey";
    public static final String STREAM_VALUE="DetroitChowStreamValue";
    
    public static final String STORAGE_FILE="DetroitChowStorageFile";
    
    @Override
    public void onCreate() {
            //reinitialize variable
    }
  
    public void registerUpdateStoresObserver(IStoreServiceObserver o){
      if(storesObservers == null)
        storesObservers = new ArrayList<IStoreServiceObserver>();
      storesObservers.add(o);
    }
    
    public void unRegisterUpdateStoresObserver(IStoreServiceObserver o){
      for (IStoreServiceObserver _o : storesObservers){
        if(_o == o){
          try{
            storesObservers.remove(o);
          }catch(Exception e){}
        }
      }
    }
    
    public void registerArticleUpdateObserver(IArticleUpdateObserver o){
      LogUtil.log("StoreService.registerArticleUpdateObserver", "registering Article Observer");
      if(articleObservers == null)
        articleObservers = new ArrayList<IArticleUpdateObserver>();
      articleObservers.add(o);
    }
    
    public void unRegisterArticleUpdateObserver(IArticleUpdateObserver o){
      for (IArticleUpdateObserver uo : articleObservers){
        if(uo == o){
          try{
            articleObservers.remove(o);
          }catch(Exception e){}
          
        }
      }
    }
    
    
      
    public void notifyObserversStoreUpdate() {
      for (IStoreServiceObserver o: storesObservers) {
        o.onStoreManagerUpdate();
      }
    }
    
    public void notifyObserversArticleUpdate(ArrayList<Article> result) {
      LogUtil.log("StoreService.notifyObserversArticleUpdate", "articleObservers.size : " + articleObservers.size());
      for (IArticleUpdateObserver o: articleObservers) {
        o.onArticleUpdate(result);
      }
    }
    
    public void initStores() {
      masterList = getMasterListFromFile();
      if(masterList == null || masterList.size() < 2){
        LogUtil.log("initStores", "init stores could not get stores");
        // could not get stores, go to detroitchow.com to get the stores list
        refreshStores();
      }else {
        //LogUtil.log("initStores", "i believe master file was succesful");
        notifyObserversStoreUpdate();
      }
        
    }
    
    public void refreshStores() {
      fetchAllStores();
    }
    
    public void getArticles(int storeId){
      LogUtil.log("StoreManager","getArticles storeId: " + storeId);
      // Make Asynchorounous call to detroitchow.com to retrieve restaurant reviews
      FetchArticlesTask fetchAllStoresTask = new FetchArticlesTask(this);
      fetchAllStoresTask.execute(String.valueOf(storeId));
    }
    
    private void fetchAllStores() {
      LogUtil.logTimer("StoreManager.fetchAllStores", Calendar.getInstance());
      // Make Ansynchrounous call to retrieve Stores List
      FetchAllStoresTask fetchAllStoresTask = new FetchAllStoresTask(this);
      fetchAllStoresTask.execute();
      
    }
    
    public void notifyFetchAllStoresComplete(ArrayList<TagCollection> result) {
      // TODO Auto-generated method stub
      LogUtil.log("StoreService", "notifyFetchComplete");
      masterList = result;
      if(masterList == null)
        LogUtil.log("notifyFetchAllStoresComplete", "masterList is null");
      else
        LogUtil.log("notifyFetchAllStoresComplete", "masterList is not null: " + masterList.size());
      buildAllList();  // This is the Whole list of Stores, Minus the group items that are not stores (i.e MGM grand, RenCen etc..)
      buildMapList(); // This is the Whole list of Stores, minus stores that are grouped (i.e. MGM Grand=Wolfgang, SaltWater and Bourbon Steak
      if(masterList != null)
        serializeMasterList(); // Save it to the System,
      notifyObserversStoreUpdate(); 
    }
    
    
  
  public ArrayList<StoreVO> getStoresList() {
      if(allList == null || allList.size() == 0 )
        buildAllList();
      else
        LogUtil.log("StoreService", "getStoresList allList is not null");
      return allList;
    }
    
    private void buildAllList() {
      allList = new ArrayList<StoreVO>();
      for(TagCollection tc : masterList) {
        if(tc.tag.equals(ALL_TAG)) {
          for(StoreVO svo : tc.stores) {
            if(svo.isListItem == 1) {
              allList.add(svo);
            }
          }
          Collections.sort(allList);
          break;
        }
      }
    }
    
    public ArrayList<StoreVO> getStoresList(String tag) {
      tagList=new ArrayList<StoreVO>();
       for(TagCollection tc : masterList) {
        if(tc.tag.equals(tag)) {
          tagList.addAll(tc.stores);
          Collections.sort(tagList);
          break;
        }
      }
      return tagList;
    }
    
    public ArrayList<StoreVO> getStoresListForMap() {
      if(mapList == null) {
        buildMapList();
      }
      return mapList;
    }
    
    // All stores where storeId=mapGroupId are displayed on the Map
    private void buildMapList() {
      
      mapList = new ArrayList<StoreVO>();
      for(TagCollection tc : masterList) {
        if(tc.tag.equals(ALL_TAG)) {
          for(StoreVO svo : tc.stores) {
            if(svo.mapGroupId == svo.storeId)
              mapList.add(svo);
          }
          break;
        }
      }
      
    }
    
    
    
    public ArrayList<String> getTags() {
      
      tags = new ArrayList<String>();
      for(TagCollection tc : masterList) {
        if(!tc.tag.equals(ALL_TAG))
        tags.add(tc.tag);
      }
      Collections.sort(tags);
      tags.add(0, ALL_TAG);
      return tags;
    }
    
    
    public String getPhone(int id) {
      for(StoreVO svo:allList){
        if(id== svo.storeId)
          return "tel:" + svo.phone;
      }
      return null;
    }
    
    public String getWeb(int id) {
      for(StoreVO svo:allList){
        if(svo.storeId == id)
          return svo.website;
      }
      return null;
    }
    
    public StoreVO getStore(String s) {
      for(StoreVO svo:allList){
        if(s.toUpperCase().equals(svo.name.toUpperCase()))
          return svo;
      }
      return null;
    }
    
    public StoreVO getStore(int id) {
      LogUtil.log("getStore", "All lsit size: " + allList.size());
      for(StoreVO svo:allList)
      {
        if(id == svo.storeId)
          return svo;
      }
      
      for(StoreVO svo:mapList)
      {
        if(id == svo.storeId)
          return svo;
      }
      
      
      return null;
    }
  
    public void onFetchArticles(ArrayList<Article> result) {
      // TODO Auto-generated method stub
    // TODO Auto-generated method stub
      LogUtil.log("StoreService onFetchArticles","");
      if(result != null)
        LogUtil.log("StoreService onFetchArticles","result was NOT null ");
      else
        LogUtil.log("StoreService onFetchArticles","result was null: ");
      notifyObserversArticleUpdate(result); 
      
    }
    
    public static String getMapAddressLine(StoreVO s) {
      if(s == null)
        return "";
      String result =null;
      if(s.address != null && s.city != null && s.state != null && s.zip != null)
        result=  s.address + "\n" + s.city + " " + s.state + " " + s.zip;
      else if(s.address != null && s.city != null && s.zip != null)
        result=  s.address + "\n" + s.city + " MI." + " " + s.zip;
      else if(s.address != null && s.city != null)
        result=  s.address + "\n" + s.city;
      else if(s.address != null)
        result=  s.address;
      
      result += "\n";
      if(s.phone != null)
        result += AppUtil.formattedPhoneNumber(s.phone) + "\n";
      
      if(s.website != null && s.website.length() > 0)
        result += s.website.replaceAll("^http://","") + "\n";
      
      return result;
    }
  
    public ArrayList<String> getAutoCompleteArray() {
      ArrayList<String> result = new ArrayList<String>();
      result.addAll(AppUtil.getAllStoreNames(allList));
      result.addAll(AppUtil.getAllCities(allList));
      result.addAll(tags);
      java.util.Collections.sort(result);
      return result;
    }
  
    public ArrayList<StoreVO> getSearch(String s) {
      ArrayList<StoreVO> result = new ArrayList<StoreVO>();
      // Search Stores for Keyword
      ArrayList<StoreVO> storeResult =AppUtil.getSearchStoreNames(allList, s);
      if(storeResult != null)
        result.addAll(storeResult);
      // Search Cities for Keyword match
      ArrayList<StoreVO> cityResult = AppUtil.getSearchCities(result, allList, s);
      
      if(cityResult != null && cityResult.size() > 0 ) {
        
        if(result.size() ==0 )
          result.addAll(cityResult);
        else
          result = mergeStoreArrayList(result, cityResult);
      }
      // Search Tags for match
      ArrayList<StoreVO> tagResult = AppUtil.getSearchTags(result,allList, s);
      if(tagResult != null && tagResult.size() > 0 ) {
        if(result.size() == 0)
          result.addAll(tagResult);
        else
          result = mergeStoreArrayList(result, tagResult);
      }
      java.util.Collections.sort(result); // StoreVO implements Compareable
      return result;
    }
  
    private ArrayList<StoreVO> mergeStoreArrayList(ArrayList<StoreVO> result,ArrayList<StoreVO> newArray) {
      // TODO Auto-generated method stub
      if(result == null || result.size() == 0) 
        return newArray;
      
      if(newArray == null || newArray.size() == 0) 
        return result;
      
      boolean hasItem = false;
      for(StoreVO newItem : newArray) {
        hasItem = false;
        for(StoreVO target : result) {
          if(newItem.name.equals(target.name)) {
            hasItem= true;
            break;
          }
        }
        if(!hasItem)
          result.add(newItem);
      }
      return result;
    }
    
    public boolean isStoresAlive() {
      if(masterList == null || allList == null)
        return false;
      return true;
    }
    // Save Masterlist to filesystem
    private void serializeMasterList() {
      // TODO Auto-generated method stub
      try{
        FileOutputStream fos = openFileOutput(STORAGE_FILE, Context.MODE_PRIVATE);
        ObjectOutputStream oo=new ObjectOutputStream(fos);
        oo.writeObject(masterList); 
        //fos.write(masterList.getBytes());
        fos.close();
      }catch(Exception e){
        // Should handle this
        
      }
    }
    
    // retrieve seriazlized object from system
    private ArrayList<TagCollection> getMasterListFromFile() {
      ArrayList<TagCollection> result =null;
      try{
        FileInputStream fileIn = openFileInput(STORAGE_FILE);
        ObjectInputStream in = new ObjectInputStream(fileIn);
        result = (ArrayList<TagCollection>) in.readObject();
        in.close();
        fileIn.close();
      }catch(Exception e){
        LogUtil.log("getMasterListFromFile", "Exception in getMasterList");
        e.printStackTrace();
        LogUtil.log("getMasterListFromFile", e.getMessage());
        return null;
      }
      
      if(result == null)
        LogUtil.log("getMasterListFromFile","result is null" );
      else
        LogUtil.log("getMasterListFromFile", "Size: " + result.size());
      return result;
    }
  
  
}
